<?php

namespace app\models;

use think\Db;
use think\Model;
use think\facade\Log;
use think\Facade\Env;

class Alipay
{
    protected $config = [
        'appid' => '2021003154679585',
        'alipay_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs3CfYfJHDZiXHZEu63OdQemLfo2M4rxksGSkqP5dbyrPieJYGnnsaNd44Fi/knwWnVZqR7QctFh6LlKrOJiAkBfj6mdLO8OaFWzOJntc9/mJNVUrYO+leGaUJFafoxSC93H2gRcudpVArOCvCkGbfOSHUr4I2dUgkioYmkVNudT4NeAUl4fyq9tYT4sEdOYUa23xdwmvhjNJQtBzxoJhXXU2dhC4yLyMsStr5D/uOKmlQeBNkBP8pKK3zZcJ2kvgCBW+ejmP4M2IOuJyPlx98ydnTJDR4CwB+O7TAVbPUI0u/7LzVvZdNTIJeCRBKrAunV5urblalhDp9pyBB2XJowIDAQAB',
        'rsa_private_key' => 'MIIEowIBAAKCAQEAncM+zUj9qTkw5i3DTfLziTkkip4eAFmDgp72pvdDjt0CisBW9kYpNS/fCs05eaAASq1FJ1BmFuQ5IIrUFHSwZjLOjoev5lH6COi4CeK1Ts48sN0xhiKTbeJibLjhgdgiGcNOtMvqe7o7o0TD2F8pIrlk+7o5HPvasAdQHVXmz5ZcnATQN0AJ+mzPw2U9KVtykH2wDBKvGbAtPTPBMiW1BR8UQztN1jyHWsP6BlrB5V/F5ErJKhOlRgjAuOAePpC2xAg8/w/L5FofaWebG0QBxAQg6JcL21mrClvr88WIQIoolwYLHXQ6Z0A/fnncZjbp2l0EaOGo1SltFm2tQ7MJiwIDAQABAoIBAGXy2S+x3nIvyiDFK/PAb46axblqSnrxXaHYdg4ZtaepvM3LU6ccWd7MC7egPWcSdX3MkObB9ucOHrif3kNF0QSKCp9iodIV4Z1GPj/tKDs/3Upx9mH3EAyvgek+ZEQTVp4Nb1w4dlP0Wju84U5+2EdfRRnDEstfGcgtQaFjdJ9qQWJejSfAwCqOs+Z1P1BsyAR+Pcah62QU5q+IAL1M+bw7bxgM8j3pqpBqmcoi16moXH3A1MfVrYSgexe7zveCHjMk+waBMSLid5fqucDUrWHUSc9N7pGKSnkLkcahUB2ORV8ftnH6wT74NBWReCyvOXTE+AJ5SM/gmQhqOJ5KEFECgYEA/sqIfuIjPKXKOb1peJKNf+sVwkwFJekMgcHO8YUj2Xla35xIxe4fyUgyNp/DJO2NVCDme5XR3C5foWPG7yl1+IVz69SMltVLgdWW4q1HADiTjcDJ5FL146MUHznlcgNcRYU3pPwyCxgbZdMBeW8YkNcOMhLJ5g3TNgCCWOiRTIMCgYEAnoLcwIRpu9XXgTqC3muPWiFNlY1sk7COU8z7NVP7eWJs8S2TPwUxqQ32Wi0DL60s9pPB6IsrQsCwbqG+/IQ0uBVSs1j4j8iYnqOSbO8KXp/RiMqOnEcVXFTNLwGI9Cjd3II2n+g/4u4CM5xQE22M5ImHQzTq6ERdAo6SAzPX0FkCgYAlpSEDtj/Ejy5Q16aEtQqJEt/VJExCbKt3ZH5B+wzv7dk6Xqn17JfvsmYQ4gwAIcUW4AGN0vX91TA19TsITRTsEjOtbWsUQhF07mzrsvRhmPpcIFb0EDnaV1b6wiAfd1Wjw+oTy1tNflNV+RYnrf1wMQYc6d/AyNwjB3I61qnZJwKBgCqy6bBaevkEU7cWAebUZptK2VFt9U+bgduFHi5xXw6Fuv8E3eNeDNCTWEdWVMNlDPWnO9bellq2+U/z9jSjkGf+Jnb1go+I48PoICuEAPEYcCM/HUdvxotGtLby3OeYkpsx4HEW6Q7COomYk+GH4yo0+eDawmSq9bsYmLCQKJSRAoGBAOrqzsEXoCSJjC2ZeNVUiwwPnJ1gLCdNHPzn5aU5BvmGSc1J/4GIEo3rDwmTL8BKJ92Hu6OMlo/ptNpAZJV4fWftIRE2zj68lG6ZhCtXcFA7FPYW7dIPzqhYFEPgFYgG+uUe7oGoKrT7EXR2bnZgJSomcrNGxM8E46xmdMsH4wC1',

    ];

    public function pay($paymentInfo)
    {
        $result = [
            'status' => false,
            'data' => [],
            'msg' => ''
        ];

        //其实就是判断是pc端支付，还是h5端支付，pc端传trade_type的值为PC，h5端传trade_type为WAP
        if (isset($paymentInfo['params']) && $paymentInfo['params'] != "") {
            $params = json_decode($paymentInfo['params'], true);
        } else {
            $params['trade_type'] = "WAP";
        }
        if (!isset($params['trade_type'])) {
            $params['trade_type'] = "WAP";
        }
        if ($params['trade_type'] != "PC" && $params['trade_type'] != "WAP" && $params['trade_type'] != "APP" && $params['trade_type'] != "JSAPI") {
            $result['msg'] = "不支持的支付模式";
            return $result;
        }
        if ($params['trade_type'] == "WAP") {
            $method = "alipay.trade.wap.pay";
            $product_code = "QUICK_WAP_WAY";
        } elseif ($params['trade_type'] == "APP") {
            $method = "alipay.trade.app.pay";
            $product_code = "QUICK_MSECURITY_PAY";
        } elseif ($params['trade_type'] == "JSAPI") { //支付宝小程序支付，调用统一下单接口
            $method = "alipay.trade.create";
            $product_code = "";
        } else {
            $method = "alipay.trade.page.pay";
            $product_code = "FAST_INSTANT_TRADE_PAY";
        }

        $url = 'https://openapi.alipay.com/gateway.do';
        //组装系统参数
        if ($params['trade_type'] == "JSAPI") {
            $data["app_id"] = getAddonsConfigVal("MPAlipay", "mp_alipay_appid");
        } else {
            $data["app_id"] = $this->config['appid'];
        }
        $data["version"] = "1.0";
        $data["format"] = "JSON";
        $data["sign_type"] = "RSA2";
        $data["method"] = $method;
        $data["timestamp"] = date("Y-m-d H:i:s");
        $notify_url = "https://" . Env('app.api_url') . '/other/callback?type=3';
        $data['notify_url'] = $notify_url;
        if ($params['trade_type'] == "PC" || $params['trade_type'] == "WAP") {
            $return_url = "";
            if (isset($paymentInfo['params']) && $paymentInfo['params'] != "") {
                $params = json_decode($paymentInfo['params'], true);
                if (isset($params['return_url'])) {
                    $return_url = $params['return_url'];
                }
            }
            $data["return_url"] = $return_url;
        }
        $data["charset"] = "utf-8";
        //业务参数
        $ydata["subject"] = mb_substr($paymentInfo['pay_title'], 0, 42, 'utf-8');          //商品名称,此处用商户名称代替
        $ydata["out_trade_no"] = $paymentInfo['payment_id'];     //平台订单号
        $ydata["total_amount"] = $paymentInfo['money'];          //总金额，精确到小数点两位
        if ($product_code != "") {
            $ydata["product_code"] = $product_code;
        }

        if ($params['trade_type'] == 'JSAPI') {
            //取open_id
            // $openid_re = $this->getOpenId($paymentInfo['user_id']);
            // if (!$openid_re['status']) {
            //     return $openid_re;
            // }
            // $ydata["buyer_id"] = $openid_re['data'];
        }
        $data["biz_content"] = json_encode($ydata, JSON_UNESCAPED_UNICODE);
        //待签名字符串
        $preSignStr = $this->getSignContent($data);

        $sign = $this->sign($preSignStr, $data['sign_type']);
        if ($params['trade_type'] == "APP") {
            $data = $preSignStr . "&sign=" . urlencode($sign);
        } elseif ($params['trade_type'] == "JSAPI") {
            //走统一下单接口
            $data = $preSignStr . "&sign=" . urlencode($sign);
            $re = $this->create($url, $data);
            if (!$re['status']) {
                return $re;
            }
            $re['data']['payment_id'] = $paymentInfo['payment_id'];
            return $re;
            //组装数据。
        } else {
            $data['sign'] = $sign;      //wap和pc传所有值，在前端模拟表单提交吧
        }

        $result['data'] = [
            'payment_id' => $paymentInfo['payment_id'],
            'url' => $url,
            'data' => $data
        ];
        $result['status'] = true;
        return $result;
    }



    public function callback()
    {
        $result = [
            'status' => false,
            'data' => [],
            'msg' => ''
        ];
        trace(input('post.'), 'alipay');
        //获取通知的数据
        $data = input('post.');
        Log::write($data, 'test');
        $sign = $data['sign'];
        $data['sign_type'] = null;
        $data['sign'] = null;
        if(!empty($data['type'])){
            unset($data['type']);
        }
        $re = $this->verify($this->getSignContent($data), $sign, "RSA2");

        if ($re) {
            if ($data['trade_status'] == 'TRADE_SUCCESS' || $data['trade_status'] == 'TRADE_FINISHED') {
                $payment =  (new Payment())->where(['paymentid' => $data['out_trade_no']])->find();
                if (empty($payment)) {
                    $result['msg'] = 'fail';
                    return $result;
                }
                if ($payment['status'] == 2) {
                    $result['status'] = true;
                    $result['msg'] = 'success';
                    return $result;
                }
                switch ($payment['type']) {
                    case '1':
                        $order = (new Order())->where('ordersn', $payment['ordersn'])->where('status', 1)->find();
                        if (empty($order)) {
                            $result['msg'] = 'fail';
                            return $result;
                        }
                        break;
                    case '2':
                        $parmes = json_decode($payment['parmes'], true) ?? [];
                        break;
                    case '3':
                        $iorder = (new IntergralOrder())->where('id', $payment['ordersn'])->where('status', 0)->find();
                        if (empty($iorder)) {
                            $result['msg'] = 'fail';
                            return $result;
                        }
                        break;
                    case '4':
                        $user = (new User())->where('id', $payment['ordersn'])->find();
                        break;
                    case '5':
                        $coupons = [];
                        $promo = (new Promo())->where('id', $payment['ordersn'])->find();
                        $promo['coupon'] = json_decode($promo['coupon'], true) ?? [];
                        $parmes = json_decode($payment['parmes'], true) ?? [];
                        $code = rekey(new Pkey(), '');
                        if (empty($code)) {
                            break;
                        }
                        break;
                    case '6':
                        $order = (new Baoming())->where('id', $payment['ordersn'])->where('status', 1)->find();
                        if (empty($order)) {
                            $result['msg'] = 'fail';
                            return $result;
                        }
                        break;
                    default:
                        $result['msg'] = 'fail';
                        return $result;
                        break;
                }
                Db::startTrans();
                try {
                    (new Payment())->where(['paymentid' => $data['out_trade_no'], 'status' => 1])
                        ->update([
                            'status' => 2,
                            'out_refund_no' => $data['transaction_id'] ?? 0,
                            'paydata' => serialize($data)
                        ]);
                    if ($payment['type'] == 1) {
                        (new Order())->where('id', $order['id'])->where('status', 1)->update(['status' => 2, 'payment' => 2, 'paytime' => date('Y-m-d H:i:s')]);
                    }
                    if ($payment['type'] == 2 && !empty($parmes)) {
                        $user = (new User())->where('id', $parmes['user_id'])->find();
                        if ($user['level'] < $parmes['level']) {
                            $endtime = time() + 86400 * $parmes['days'];
                        } else {
                            $endtime = strtotime($user['level_time']) + 86400 * $parmes['days'];
                        }
                        (new User())->where('id', $parmes['user_id'])->update(['level' => $parmes['level'], 'level_time' => date('Y-m-d', $endtime)]);
                        $mycoupon = [];
                        foreach ($parmes['coupon'] as $coupon) {
                            $mycoupon[] = [
                                'user_id' => $parmes['user_id'],
                                'getmoney' => $coupon['getmoney'],
                                'money' => $coupon['money'],
                                'status' => 1,
                                'remark' => '购买会员赠送',
                                'text' => '本券由平台发放，最终解释权归平台所有',
                                'endtime' => date('Y-m-d H:i:s', time() + 86400 * $coupon['days'])
                            ];
                        }
                        if (count($mycoupon) > 0) {
                            (new Coupon())->saveAll($mycoupon);
                        }
                    }
                    if ($payment['type'] == 3) {
                        (new IntergralOrder())->where('id', $payment['ordersn'])->where('status', 0)->update(['status' => 1, 'payment' => 1]);
                    }
                    if ($payment['type'] == 4 && $payment['money'] > 0) {
                        (new User())->MondyEdit($user['id'], $user['money'], $payment['money'], 1, 0, '充值到账');
                    }
                    if ($payment['type'] == 5) {
                        //插入秘钥记录
                        $keyid = (new Pkey())->insertGetId(['promoid' => $promo['id'], 'code' => $code, 'type' => 2, 'status' => 2, 'user_id' => $parmes['user_id']]);
                        foreach ($promo['coupon'] as $coupon) {
                            $coupon['type'] = implode(',', $coupon['type']);
                            $coupons[] = [
                                'type' => 1,
                                'types' => 2,
                                'user_id' => $parmes['user_id'],
                                'agentid' => $promo['agentid'],
                                'productid' => $coupon['type'],
                                'system' => 5,
                                'number' => 1,
                                'status' => 1,
                                'endtime' => date('Y-m-d H:i:s', time() + 86400 * $coupon['days']),
                                'promo' => $promo['id'],
                                'keyid' => $keyid,
                                'btmoney' => $coupon['money']
                            ];
                        }
                        if (count($coupons) > 0) {
                            (new Coupon())->allowfield(true)->saveAll($coupons);
                        }
                    }
                    if ($payment['type'] == 6) {
                        (new Baoming())->where('id', $order['id'])->where('status', 1)->update(['status' => 2, 'paymentid' => $data['out_trade_no']]);
                    }
                    Db::commit();
                } catch (\Exception $e) {
                    Log::write($e->getMessage(), 'test');
                    Db::rollback();
                    $result['msg'] = 'fail';
                    return $result;
                }
                $result['status'] = true;
                $result['msg'] = 'success';
            }
            $result['msg'] = 'success';
        } else {
            $result['msg'] = "fail";
        }
        return $result;
    }

    public function refund($refundInfo, $paymentInfo)
    {
        $result = [
            'status' => false,
            'data' => [],
            'msg' => ''
        ];
    }

    //支付宝统一下单接口
    private function create($url, $data)
    {
        $curl = new Curl();
        $re = $curl::post($url, $data);
        $re = json_decode($re, true);

        if (!isset($re['alipay_trade_create_response']['code'])) {
            return error_code(10000);
        }
        if ($re['alipay_trade_create_response']['code'] != '10000') {
            return [
                'status' => false,
                'data' => $re['alipay_trade_create_response'],
                'msg' => $re['alipay_trade_create_response']['sub_msg']
            ];
        }
        return [
            'status' => true,
            'data' => [
                'out_trade_no' => $re['alipay_trade_create_response']['out_trade_no'],
                'trade_no' => $re['alipay_trade_create_response']['trade_no']
            ],
            'msg' => ''
        ];
    }




    private function verify($data, $sign, $signType = 'RSA')
    {
        $pubKey = $this->config['alipay_public_key'];
        $res = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($pubKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
        //调用openssl内置方法验签，返回bool值
        if ("RSA2" == $signType) {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
        } else {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        }
        trace($result, 'alipay');
        return $result;
    }


    private function sign($data, $signType = "RSA")
    {
        $priKey = $this->config['rsa_private_key'];
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($priKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";

        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    private function getSignContent($params)
    {
        ksort($params);

        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        unset($k, $v);
        return $stringToBeSigned;
    }
    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     **/
    private function checkEmpty($value)
    {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;

        return false;
    }


    /**
     * 加密方法
     * @param string $str
     * @return string
     */
    private function encrypt($str, $screct_key)
    {
        //AES, 128 模式加密数据 CBC
        $screct_key = base64_decode($screct_key);
        $str = trim($str);
        $str = $this->addPKCS7Padding($str);
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), 1);
        $encrypt_str =  mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $screct_key, $str, MCRYPT_MODE_CBC);
        return base64_encode($encrypt_str);
    }

    /**
     * 解密方法
     * @param string $str
     * @return string
     */
    private function decrypt($str, $screct_key)
    {
        //AES, 128 模式加密数据 CBC
        $str = base64_decode($str);
        $screct_key = base64_decode($screct_key);
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), 1);
        $encrypt_str =  mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $screct_key, $str, MCRYPT_MODE_CBC);
        $encrypt_str = trim($encrypt_str);

        $encrypt_str = $this->stripPKSC7Padding($encrypt_str);
        return $encrypt_str;
    }

    /**
     * 填充算法
     * @param string $source
     * @return string
     */
    private function addPKCS7Padding($source)
    {
        $source = trim($source);
        $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);

        $pad = $block - (strlen($source) % $block);
        if ($pad <= $block) {
            $char = chr($pad);
            $source .= str_repeat($char, $pad);
        }
        return $source;
    }
    /**
     * 移去填充算法
     * @param string $source
     * @return string
     */
    private function stripPKSC7Padding($source)
    {
        $source = trim($source);
        $char = substr($source, -1);
        $num = ord($char);
        if ($num == 62) return $source;
        $source = substr($source, 0, -$num);
        return $source;
    }
}
